package utils;

import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

/**
 * 用来测试基于Spring配置的含有数据库事务支持的Service及Dao. Convenient superclass for tests that
 * should occur in a transaction, but normally will roll the transaction back on
 * the completion of each test.
 * <p>
 * This is useful in a range of circumstances, allowing the following benefits:
 * <ul>
 * <li>Ability to delete or insert any data in the database, without affecting
 * other tests
 * <li>Providing a transactional context for any code requiring a transaction
 * <li>Ability to write anything to the database without any need to clean up.
 * </ul>
 */
public class TransactalSpringComponentTests {
    /**
     * Logger for this class.
     */
    protected static final Logger logger = Logger
            .getLogger(TransactalSpringComponentTests.class);

    /** spring ApplicationContext. */
    protected static AbstractApplicationContext springCtx;

    /** spring transaction manager. */
    protected static PlatformTransactionManager transactionManager;

    /**
     * TransactionStatus for this test. Typical subclasses won't need to use it.
     */
    protected TransactionStatus transactionStatus;

    /**
     * Number of transactions started.
     */

    private int transactionsStarted;

    /**
     * 取得spring 配置文件名.
     * @return String[]
     */
    public static String[] getSpringConfigFils() {
        String springFilePattern = "*.spring.xml";
        return new String[] { springFilePattern };
    }

    /**
     * 装载Spring环境.
     */
    @BeforeClass
    public static void init() {
        try {
            // 装载spring配置文件
            springCtx = new ClassPathXmlApplicationContext(
                    getSpringConfigFils());
            transactionManager = (PlatformTransactionManager) springCtx
                    .getBean("transactionManager");
            if (transactionManager == null) {
                throw new IllegalArgumentException(
                        "no transactionManager bean defined in spring xml files");
            }
        } catch (Exception e) {
            logger.error("init err ", e);
        }
    }

    /**
     * 每个测试方法调用前创建新的Transaction.
     */
    @Before
    public void createNewTrans() {
        if (transactionStatus != null) {
            throw new IllegalStateException(
                    "Cannot start new transaction without ending existing transaction:"
                            + "Invoke endTransaction() before startNewTransaction()");
        }

        transactionStatus = transactionManager
                .getTransaction(new DefaultTransactionDefinition());
        ++transactionsStarted;

        logger.info("Began transaction (" + transactionsStarted
                + "): transaction manager [" + transactionManager + "]");

    }

    /**
     * 每个测试方法调用后回滚Transaction.
     */
    @After
    public void rollbackTrans() {
        if (transactionStatus != null) {
            try {
                transactionManager.rollback(transactionStatus);
                logger.info("Rolled back transaction after test execution");
            } finally {
                transactionStatus = null;
            }
        }
    }

    /**
     * 销毁Spring context.
     */
    @AfterClass
    public static void destroy() {
        springCtx.destroy();
    }

}
